<script lang="tsx">
  import {
    defineComponent,
    resolveComponent,
    h,
    unref,
    computed,
    watch,
    toRaw,
  } from 'vue';

  import type { PropType, Slots } from 'vue';

  import { FormItem } from '@arco-design/web-vue';

  import { isObject, isFunction, isString, isArray } from '@/utils/is';
  import type { FormFiled, Events, PropsCol } from './props';

  export default defineComponent({
    components: {
      'a-form-item': FormItem,
    },
    props: {
      formFiledItem: {
        type: Object as PropType<FormFiled>,
        default() {
          return {};
        },
      },
      formModel: {
        type: Object,
        default() {
          return {};
        },
      },
      labelWidth: {
        type: String as PropType<string>,
      },
      showColon: {
        type: Boolean as PropType<boolean>,
      },
      formDiabled: {
        type: Boolean as PropType<boolean>,
        default: false,
      },
      formFiledList: {
        type: Array as PropType<FormFiled[]>,
      },
      formFiledListLocal: {
        type: Array as PropType<FormFiled[]>,
      },
    },
    emits: ['updateFiled', 'resetFiled', 'validateField'],
    setup(props, { slots, emit }) {
      // 更新
      const updateFiled = (data: FormFiled | FormFiled[]) => {
        emit('updateFiled', data);
      };

      // 重置
      const resetFiled = (data: string | string[]) => {
        emit('resetFiled', data);
      };

      const validateField = (field: string, callback: any) => {
        emit('validateField', field, callback);
      };

      const getValues = computed(() => {
        return {
          formModel: props.formModel,
          formFiledItem: props.formFiledItem,
          formFiledListLocal: props.formFiledListLocal,
          formFiledList: props.formFiledListLocal,
          formDiabled: props.formDiabled,
          updateFiled,
          resetFiled,
          validateField,
        };
      });

      if (props.formFiledItem.onFieldValueChange) {
        props.formFiledItem.onFieldValueChange.forEach((item) => {
          const options = item.options || {};
          watch(
            () => {
              const { keys } = item;
              const filedValue = isArray(keys)
                ? keys.map((t) => {
                    return props.formModel[t];
                  })
                : props.formModel[keys];
              return filedValue;
            },
            (newVal, oldVal) => {
              const { handle } = item;
              handle({ newVal, oldVal, ...unref(getValues) });
            },
            options
          );
        });
      }

      const getShow = (): boolean => {
        if (
          props.formFiledItem?.ifShow &&
          isFunction(props.formFiledItem?.ifShow)
        ) {
          return props.formFiledItem?.ifShow(unref(getValues));
        }

        return true;
      };

      const getSlot = (conponentSlots: Slots, slot: unknown) => {
        if (!slot) return null;
        if (isString(slot)) {
          const fun = conponentSlots[slot as string];
          if (!fun) return null;
          return fun(unref(getValues));
        }
        if (isFunction(slot)) {
          return (slot as any)(unref(getValues));
        }
        return null;
      };

      // 获取 component props
      const getComponentsProps = computed(() => {
        const { formFiledItem } = props;
        if (!formFiledItem?.props?.component) return {};
        if (isFunction(formFiledItem.props.component)) {
          return formFiledItem.props.component(unref(getValues));
        }
        return formFiledItem?.props?.component;
      });

      // 获取 formItem props
      const getFormItemProps = computed(() => {
        const { formFiledItem } = props;
        if (!formFiledItem?.props?.formItem) return {};
        if (isFunction(formFiledItem.props.formItem)) {
          return formFiledItem.props.formItem(unref(getValues));
        }

        return formFiledItem?.props?.formItem;
      });

      // 获取 col props
      const getColProps = computed<PropsCol>(() => {
        const { formFiledItem } = props;
        if (!formFiledItem?.props?.col) return {};
        if (isFunction(formFiledItem.props.col)) {
          return formFiledItem.props.col(unref(getValues));
        }

        return formFiledItem?.props?.col;
      });

      // 绑定事件
      // 此处事件会覆盖props中的事件
      const makeEvent = () => {
        if (
          props?.formFiledItem?.events &&
          isObject(props.formFiledItem.events) &&
          Object.keys(props.formFiledItem.events).length > 0
        ) {
          const obj = Object.keys(props.formFiledItem.events).reduce(
            (total: Record<string, any>, t: string) => {
              const key = `on${t[0].toUpperCase()}${t.slice(1)}`;
              total[key] = (...componentVal: any) => {
                (props.formFiledItem.events as Events)[t]({
                  ...unref(getValues),
                  componentVal,
                });
              };
              return total;
            },
            {}
          );

          return obj;
        }
        return {};
      };

      // 获取form 内表单组件
      const getComponent = () => {
        if (props.formFiledItem?.slots?.component) {
          return getSlot(slots, props.formFiledItem?.slots?.component);
        }

        let ComponentName: any;
        if (isString(props.formFiledItem?.component)) {
          ComponentName = h(
            resolveComponent((props.formFiledItem?.component as string) || '')
          );
        } else {
          ComponentName = toRaw(props.formFiledItem.component);
        }
        const inComponentSlots = props.formFiledItem?.slots?.inComponentSlots;

        const slotObj: any = {};
        if (inComponentSlots && Object.keys(inComponentSlots).length) {
          Object.keys(inComponentSlots).forEach((t) => {
            slotObj[t] = getSlot(slots, inComponentSlots[t]);
          });
        }

        const filed = props.formFiledItem.filed as string;

        return (
          <ComponentName
            allow-clear
            {...unref(getComponentsProps)}
            {...makeEvent()}
            v-model={props.formModel[filed]}
            v-slots={slotObj}
          ></ComponentName>
        );
      };

      return () => {
        const getContent = () => {
          if (props.formFiledItem?.slots?.formItem) {
            return getSlot(slots, props.formFiledItem?.slots?.formItem);
          }
          const { filed } = props.formFiledItem;

          return (
            <a-form-item
              label={props.formFiledItem.label}
              field={filed}
              showColon={props.showColon}
              label-col-flex={
                props.formFiledItem.labelWidth || props.labelWidth
              }
              {...unref(getFormItemProps)}
              row-class={props.formFiledItem.rowClass}
              v-slots={{
                label: getSlot(slots, props.formFiledItem?.slots?.label),
                help: getSlot(slots, props.formFiledItem?.slots?.help),
                extra: getSlot(slots, props.formFiledItem?.slots?.extra),
                default: () => getComponent(),
              }}
            ></a-form-item>
          );
        };

        return getShow() ? (
          <a-col {...unref(getColProps)}>{getContent()}</a-col>
        ) : null;
      };
    },
  });
</script>
